home *** CD-ROM | disk | FTP | other *** search
- /* @(#)util/pathmerge.c 1.4 9/6/92 01:10:35 */
- /*
- * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
- * Copyright (C) 1992 Ronald S. Karr
- *
- * See the file COPYING, distributed with smail, for restriction
- * and warranty information.
- */
-
- /*
- * pathmerge - PATH-FILE MERGING PROGRAM
- *
- * This program takes a set of sorted path files, as produced
- * by pathalias, and generates on the standard output a merge
- * of the path information, with one path given for each
- * hostname. Precedence in preferred paths goes to the files
- * given earliest in the argument list. One of the filenames
- * given in the argument list can be `-' for the standard
- * input.
- */
- #include <stdio.h>
- #include <ctype.h>
- #include "defs.h"
-
- extern int errno; /* last system error */
- char *malloc(); /* our storage allocator */
- char *program; /* who we are */
-
- #define MAXLINE (4096+1+1) /* max line length + newline + NULL */
-
- #define TRUE 1
- #define FALSE 0
-
- #define lower(c) ((char)(lowercase(toascii((int)(c))))) /* safe tolower */
-
- /*
- * file state
- */
- enum state {
- eof, /* the file has reached EOF */
- readme, /* the file needs to be read */
- ready /* the file has a line ready */
- };
-
- main(argc,argv)
- register int argc;
- char *argv[];
- {
- char **line; /* current input line, per file */
- enum state *status; /* end, readme or ready state, per-file */
- FILE **fd; /* per-file file descriptors */
- register int i; /* index */
- int eofs; /* number of files at EOF */
- char *previous; /* the previous line written */
- int next; /* the next line to be written */
-
- /*
- * arg check
- */
- if (argc <= 1) {
- /* no arguments, do nothing */
- exit(0);
- } else {
- program = argv[0]; /* remember who we are */
- ++argv; /* ignore argv[0] */
- --argc;
- }
-
- /*
- * malloc our arrays
- */
- /* input buffers */
- line = (char **)malloc((argc+1) * sizeof(char **));
- if (line == NULL) {
- perror(program);
- exit(errno);
- }
- for (i=0; i < argc+1; ++i) { /* line[argc] is also previous */
- line[i] = (char *)malloc((MAXLINE+1) * sizeof(char));
- if (line[i] == NULL) {
- perror(program);
- exit(errno);
- }
- /* note line[i][MAXLINE-1] is initially '\0' */
- }
- previous = line[argc]; /* note previous is an empty string */
- /* file states */
- status = (enum state *)malloc(argc * sizeof(enum state *));
- if (status == NULL) {
- perror(program);
- exit(errno);
- }
- /* per-file file descriptors */
- fd = (FILE **)malloc(argc * sizeof(FILE **));
- if (fd == NULL) {
- perror(program);
- exit(errno);
- }
-
- /*
- * open our files, assign buffers and eof status
- */
- for (i = 0; i < argc; i++) {
- /* open the file */
- if (strcmp(argv[i], "-") == 0) {
- fd[i] = stdin; /* use standard input */
- } else if ((fd[i] = fopen(argv[i], "r")) == NULL) {
- fprintf(stderr, "%s: ", program);
- perror(argv[i]);
- exit(errno);
- }
- /* enable the read on this file */
- status[i] = readme;
- }
-
- /*
- * if only one file, pass it thru and exit
- */
- if (argc == 1) {
- /* only one file to merge */
- while ((i = getc(fd[0])) != EOF) {
- putchar(i);
- }
- exit(0);
- }
-
- /*
- * sort the files
- */
- eofs = 0; /* no files at EOF */
- for (;;) {
- /*
- * read lines on files that need reading
- */
- for (i = 0; i < argc; i++) {
- /* only look at files that need to be read */
- if (status[i] != readme) {
- continue;
- }
- /* read a line from the file */
- eofs = readline(argv, fd, line, status, eofs, i);
- }
- /* if all files then done, stop */
- if (eofs >= argc) {
- break;
- }
-
- /*
- * find the alphabetically least line
- */
- for (next = -1, i = 0; i < argc; i++) {
- /*
- * only look at lines that are ready
- * if current line is earler than before, note it
- */
- if (status[i] == ready &&
- ((next < 0) || cmp(line[i], line[next]) < 0)) {
- next = i; /* our newest early line */
- }
- }
-
- /*
- * if we found a next line, print it
- */
- if (next >= 0) {
- /* write the line if it is different */
- if (cmp(previous, line[next]) != 0) {
- /* write out that line */
- fputs(line[next], stdout);
- /* save it */
- strncpy(previous, line[next], MAXLINE);
- }
- status[next] = readme; /* ready to read again */
- /* skip lines that are equal to the selected one */
- for (i=next+1; i < argc; ++i) {
- if (status[i] == ready &&
- cmp(line[i], previous) == 0) {
- status[i] = readme;
- }
- }
- }
- }
-
- /*
- * all done
- */
- exit(0);
- }
-
- /*
- * readline - read a line from a file and check for EOFs and long lines
- *
- * returns the new EOFs count
- */
- int
- readline(name, fd, line, status, eofs, which)
- char *name[]; /* the names of the files */
- FILE **fd; /* per-file file descriptors */
- char **line; /* current input line, per file */
- enum state *status; /* end, readme or ready state, per-file */
- int eofs; /* number of files on EOF state */
- int which; /* which file is being read */
- {
- int i; /* char buffer */
-
- /*
- * read the line
- *
- * If the line is MAXLINE-2 chars + newline, then line[MAXLINE-1]
- * will be '\0'. Any line that is longer will alter line[MAXLINE-1].
- */
- if (fgets(line[which], MAXLINE+1, fd[which]) != NULL) {
- /*
- * we got a line, is it a good one?
- */
- if (line[which][MAXLINE-1] != '\0') {
- /* the line was too long, skip it */
- fprintf(stderr, "%s: %s line too long\n",
- program, name[which]);
- while ((i = getc(fd[which])) != '\n') {
- /* watch for EOF */
- if (i == EOF) {
- status[which] = eof;
- return(eofs+1); /* nothing left */
- }
- }
- /* clear for next read */
- line[which][MAXLINE-1] = '\0';
- status[which] = readme; /* try again later */
- } else {
- /* we have a line to use */
- status[which] = ready;
- }
- } else {
- /* we read the end of the file */
- status[which] = eof;
- ++eofs;
- }
- return(eofs); /* return EOF count */
- }
-
- /*
- * cmp - compare two pathalias lines
- *
- * compare two strings up to the first tab (or space) character
- * to determine if the first is less than, equal to,
- * or greater than the second, returning -1, 0 and 1
- * respectively.
- */
- int
- cmp(s, t)
- register char *s; /* first line */
- register char *t; /* second line */
- {
- /*
- * look for an irregularity
- */
- while (*s == *t && !isspace(*s) && *s != '\0' && *s != ':') {
- ++s;
- ++t;
- }
- /* we have stopped at the end of the string, or on the tab */
-
- /*
- * determine the relationship between the lines
- */
- /* if we matched all the way, then note a line match */
- if ((isspace(*s) || *s == ':' || *s == '\0') &&
- (isspace(*t) || *t == ':' || *t == '\0'))
- {
- return (0);
- /* if the first line ended early, it is before the second */
- } else if (isspace(*s) || *s == '\0' || *s == ':') {
- return (-1);
- /* if the second line ended early, it is before the first */
- } else if (isspace(*t) || *t == '\0' || *t == ':') {
- return (1);
- /* we hit a difference spot, note which is earler */
- } else {
- return ((lower(*s) < lower(*t)) ? -1 : 1);
- }
- }
-